<template>
  <initVanta 
    :effect="selectedEffect"
    :enabled="effectsEnabled"
    ref="vantaBg"
  />
  <div>
    <div class="content-overlay">
      <div class="header">
        <div class="modern-button" @click="openMarket">
          <button class="btn-modern market-btn">
            <div class="btn-icon">
              <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path d="M19 7H16V6C16 3.79 14.21 2 12 2S8 3.79 8 6V7H5C3.9 7 3 7.9 3 9V20C3 21.1 3.9 22 5 22H19C20.1 22 21 21.1 21 20V9C21 7.9 20.1 7 19 7ZM10 6C10 4.9 10.9 4 12 4S14 4.9 14 6V7H10V6ZM19 20H5V9H19V20ZM12 17C13.1 17 14 16.1 14 15S13.1 13 12 13S10 13.9 10 15S10.9 17 12 17Z" fill="currentColor"/>
              </svg>
            </div>
            <span class="btn-text">Market+</span>
          </button>
        </div>
        <div class="modern-button" @click="openSettings">
          <button class="btn-modern settings-btn">
            <div class="btn-icon">
              <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path d="M19.14 12.94C19.18 12.64 19.2 12.33 19.2 12S19.18 11.36 19.14 11.06L21.16 9.48C21.34 9.34 21.39 9.07 21.28 8.87L19.28 5.33C19.17 5.13 18.9 5.07 18.7 5.17L16.35 6.27C15.81 5.87 15.22 5.56 14.58 5.32L14.22 2.81C14.19 2.58 14 2.4 13.75 2.4H9.75C9.5 2.4 9.31 2.58 9.28 2.81L8.92 5.32C8.28 5.56 7.69 5.87 7.15 6.27L4.8 5.17C4.6 5.07 4.33 5.13 4.22 5.33L2.22 8.87C2.11 9.07 2.16 9.34 2.34 9.48L4.36 11.06C4.32 11.36 4.3 11.67 4.3 12S4.32 12.64 4.36 12.94L2.34 14.52C2.16 14.66 2.11 14.93 2.22 15.13L4.22 18.67C4.33 18.87 4.6 18.93 4.8 18.83L7.15 17.73C7.69 18.13 8.28 18.44 8.92 18.68L9.28 21.19C9.31 21.42 9.5 21.6 9.75 21.6H13.75C14 21.6 14.19 21.42 14.22 21.19L14.58 18.68C15.22 18.44 15.81 18.13 16.35 17.73L18.7 18.83C18.9 18.93 19.17 18.87 19.28 18.67L21.28 15.13C21.39 14.93 21.34 14.66 21.16 14.52L19.14 12.94ZM11.75 15.5C9.68 15.5 8 13.82 8 11.75S9.68 8 11.75 8S15.5 9.68 15.5 11.75S13.82 15.5 11.75 15.5Z" fill="currentColor"/>
              </svg>
            </div>
            <span class="btn-text">Settings</span>
          </button>
        </div>
        <div class="modern-button" @click="checkNodeEnvironment">
          <button class="btn-modern node-btn" :class="{ 'checking': isCheckingNode }">
            <div class="btn-icon">
              <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path d="M12 2L2 7L12 12L22 7L12 2Z" fill="currentColor"/>
                <path d="M2 17L12 22L22 17" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
                <path d="M2 12L12 17L22 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
              </svg>
            </div>
            <span class="btn-text">{{ nodeStatus.isInstalled ? 'Node ✓' : 'Node' }}</span>
          </button>
        </div>
        <themeButton 
          @color-change="handleThemeColorChange" 
          @effect-change="handleEffectChange"
          @settings-change="handleSettingsChange"
          @effects-toggle="handleEffectsToggle"
        />
      </div>
      <div class="title">
        <h1>Se!ect a Function Plugin</h1>
      </div>
      <div class="main-content">
        <!-- 修改卡片容器结构 -->
        <div 
          v-for="([category, plugins], categoryIndex) in Object.entries(groupedPlugins)" 
          :key="category"
          class="category-group category-animate"
          :style="{ animationDelay: categoryIndex * 0.2 + 's' }"
        >
          <div class="category-header category-title-animate" :style="{ animationDelay: categoryIndex * 0.2 + 0.1 + 's' }">
            <h2>{{ category }}</h2>
          </div>
          <div class="card-container">
            <card-temp
              v-for="(plugin, pluginIndex) in plugins"
              :key="plugin.id"
              :id="plugin.id"
              :name="plugin.name"
              :description="plugin.description"
              :version="plugin.version"
              :category="plugin.category"
              :showDelete="true"
              class="plugin-card-animate"
              :style="{ animationDelay: (categoryIndex * 0.2 + pluginIndex * 0.1 + 0.3) + 's' }"
              @click="navigateToTemplate(plugin.id)"
              @delete="deletePlugin(plugin)"
            />
          </div>
        </div>
        
        <!-- 空状态提示 -->
        <div v-if="Object.keys(groupedPlugins).length === 0" class="empty-state empty-state-animate">
          <div class="empty-icon">📦</div>
          <h3>暂无插件</h3>
          <p>前往插件市场下载您需要的功能插件</p>
          <button class="empty-action-btn" @click="openMarket">
            浏览插件市场
          </button>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import themeButton from '../components/themeButton.vue';
import { ref, onMounted, watch, computed } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import CardTemp from '../components/cardTemp.vue';
import initVanta from '../components/initVanta.vue';
import { Plugin } from '../store/PluginInterface';
import { ElMessage } from 'element-plus';
import { useVanta } from '../components/useVanta';

const { selectedColor, selectedEffect, hexToNumber } = useVanta();

const vantaData = useVanta();

// 获取 initVanta 组件实例
const vantaBg = ref();
// 特效开关状态
const effectsEnabled = ref(localStorage.getItem('effectsEnabled') !== 'false');
// 处理主题颜色变化
const handleThemeColorChange = (color: string) => {
  selectedColor.value = color;
  localStorage.setItem('themeColor', color);
};

// 处理 Vanta 效果变化
const handleEffectChange = (effect: string) => {
  selectedEffect.value = effect;
  localStorage.setItem('vantaEffect', effect);
};

// 处理效果设置变化
const handleSettingsChange = (settings: any) => {
  // console.log('Settings changed:', settings);
  // 当设置变化时，重新初始化 Vanta 效果
  if (vantaBg.value) {
    vantaBg.value.setColor();
  }
};

// 处理特效开关
const handleEffectsToggle = (enabled: boolean) => {
  effectsEnabled.value = enabled;
  localStorage.setItem('effectsEnabled', enabled.toString());
  
  // 切换背景样式
  const contentOverlay = document.querySelector('.content-overlay') as HTMLElement;
  if (contentOverlay) {
    if (enabled) {
      // 开启特效时移除深灰色背景
      contentOverlay.style.backgroundColor = '';
    } else {
      // 关闭特效时设置深灰色背景
      contentOverlay.style.backgroundColor = '#242424';
    }
  }
};

declare const pluginAPI: any;
declare const nodeAPI: any;

const route = useRoute();
const router = useRouter();
const userPlugins = ref<Plugin[]>([]);

// Node.js环境检查相关状态
const isCheckingNode = ref(false);
const nodeStatus = ref({
  isInstalled: false,
  version: '',
  npmVersion: '',
  isPortable: false
});
// 缓存键：保存最近一次检查到的 Node 环境状态用于显示
const NODE_STATUS_KEY = 'nodeStatusCache';
const installProgress = ref({
  stage: '',
  progress: 0,
  message: ''
});

// 加载用户插件
const loadUserPlugins = () => {
  console.log('加载用户插件...');
  const plugins = localStorage.getItem('userPlugins');
  if (plugins) {
    userPlugins.value = JSON.parse(plugins);
    console.log('已加载插件:', userPlugins.value);
  } else {
    userPlugins.value = [];
    console.log('无本地插件');
  }
};

const navigateToTemplate = (value: string) => {
  console.log('导航至插件:', value);

  // 查找对应的插件
  const plugin = userPlugins.value.find(p => p.id === value);
  if (plugin) {
    // 如果找到插件，则导航到插件页面
    router.push(`/plugins/${plugin.id}`);
  } else {
    // 如果没有找到插件，则导航到对应页面
    router.push(`/${value}`);
  }
};

// 新增计算属性按分类分组
const groupedPlugins = computed(() => {
  return userPlugins.value.reduce((groups: Record<string, Plugin[]>, plugin) => {
    const category = plugin.category || '未分类';
    if (!groups[category]) {
      groups[category] = [];
    }
    groups[category].push(plugin);
    return groups;
  }, {} as Record<string, Plugin[]>);
});

// 删除插件
const deletePlugin = (plugin: Plugin) => {
  try {
    // 先删除本地插件文件夹
    pluginAPI.deletePlugin(plugin.id);
    
    // 再更新本地存储
    userPlugins.value = userPlugins.value.filter(p => p.id !== plugin.id);
    localStorage.setItem('userPlugins', JSON.stringify(userPlugins.value));
    ElMessage.success('插件删除成功');
  } catch (error) {
    console.error('删除插件失败:', error);
    ElMessage.error('删除插件失败');
  }
};


onMounted(async () => {
  // 确保组件初始化时应用保存的设置
  if (vantaBg.value) {
    vantaBg.value.setColor();
  }
  loadUserPlugins();
  
  // 初始化背景状态
  const contentOverlay = document.querySelector('.content-overlay') as HTMLElement;
  if (contentOverlay && !effectsEnabled.value) {
    contentOverlay.style.backgroundColor = '#242424';
  }

  // 优先从缓存恢复上次检查的 Node 状态用于按钮文案展示（不发起实际检查）
  try {
    const cached = localStorage.getItem(NODE_STATUS_KEY);
    if (cached) {
      nodeStatus.value = JSON.parse(cached);
    }
  } catch (e) {
    console.warn('恢复缓存的 Node 状态失败:', e);
  }
  
  // 仅在安装后第一次打开时进行一次静默检查，之后需手动点击按钮检查
  const FIRST_CHECK_KEY = 'nodeFirstCheckDone';
  if (!localStorage.getItem(FIRST_CHECK_KEY)) {
    try {
      await initNodeCheck();
    } finally {
      // 无论检查成功与否，都标记已进行过首次检查
      localStorage.setItem(FIRST_CHECK_KEY, 'true');
    }
  }
});

// 监听路由变化
watch(() => route.path, (newPath, oldPath) => {
  console.log('Route changed:', oldPath, '->', newPath);
  if (newPath === '/') {
    loadUserPlugins();
  }
});

const openMarket = () => {
  router.push('/market');
};

const openSettings = () => {
  router.push('/settings');
};

// Node.js环境检查函数
const checkNodeEnvironment = async () => {
  if (isCheckingNode.value) return;
  
  isCheckingNode.value = true;
  
  try {
    ElMessage.info('正在检查Node.js环境...');
    
    const result = await nodeAPI.checkNodeEnvironment();
    
    if (result.success) {
      nodeStatus.value = result.data;
      // 缓存检查结果用于后续会话的展示
      localStorage.setItem(NODE_STATUS_KEY, JSON.stringify(result.data));
      
      if (result.data.isInstalled) {
        ElMessage.success(`Node.js已安装 (版本: ${result.data.version})`);
      } else {
        // Node.js未安装，询问是否安装
        const shouldInstall = await showInstallConfirmDialog();
        if (shouldInstall) {
          await installNodeJS();
        }
      }
    } else {
      ElMessage.error(`检查Node.js环境失败: ${result.error}`);
    }
  } catch (error: any) {
    console.error('检查Node.js环境出错:', error);
    ElMessage.error(`检查Node.js环境出错: ${error.message}`);
  } finally {
    isCheckingNode.value = false;
  }
};

// 显示安装确认对话框
const showInstallConfirmDialog = (): Promise<boolean> => {
  return new Promise((resolve) => {
    ElMessage({
      message: 'Node.js未安装，是否安装便携版Node.js？',
      type: 'warning',
      duration: 0,
      showClose: true,
      customClass: 'node-install-confirm',
      onClose: () => resolve(false)
    });
    
    // 创建确认按钮
    setTimeout(() => {
      const messageEl = document.querySelector('.node-install-confirm');
      if (messageEl) {
        const buttonContainer = document.createElement('div');
        buttonContainer.style.marginTop = '10px';
        buttonContainer.innerHTML = `
          <button id="confirm-install" style="margin-right: 10px; padding: 5px 15px; background: #409EFF; color: white; border: none; border-radius: 4px; cursor: pointer;">安装</button>
          <button id="cancel-install" style="padding: 5px 15px; background: #909399; color: white; border: none; border-radius: 4px; cursor: pointer;">取消</button>
        `;
        messageEl.appendChild(buttonContainer);
        
        document.getElementById('confirm-install')?.addEventListener('click', () => {
          (messageEl.querySelector('.el-message__closeBtn') as HTMLElement)?.click();
          resolve(true);
        });
        
        document.getElementById('cancel-install')?.addEventListener('click', () => {
          (messageEl.querySelector('.el-message__closeBtn') as HTMLElement)?.click();
          resolve(false);
        });
      }
    }, 100);
  });
};

// 安装Node.js
const installNodeJS = async () => {
  let progressMessage: any = null;
  
  try {
    // 创建一个持久的进度消息
    progressMessage = ElMessage({
      message: '开始安装便携版Node.js...',
      type: 'info',
      duration: 0, // 不自动关闭
      showClose: false
    });
    
    // 监听安装进度
    nodeAPI.onNodeInstallProgress((progress: any) => {
      installProgress.value = progress;
      // 更新现有消息的内容
      if (progressMessage) {
        progressMessage.message = `${progress.stage}: ${progress.message} (${progress.progress}%)`;
      }
    });
    
    const result = await nodeAPI.installPortableNode();
    
    // 关闭进度消息
    if (progressMessage) {
      progressMessage.close();
      progressMessage = null;
    }
    
    if (result.success) {
      ElMessage.success('Node.js安装成功！');
      // 重新检查环境
      await checkNodeEnvironment();
    } else {
      ElMessage.error(`Node.js安装失败: ${result.error}`);
    }
  } catch (error: any) {
    console.error('安装Node.js出错:', error);
    // 关闭进度消息
    if (progressMessage) {
      progressMessage.close();
      progressMessage = null;
    }
    ElMessage.error(`安装Node.js出错: ${error.message}`);
  } finally {
    // 清理事件监听器
    nodeAPI.removeNodeListeners();
  }
};

// 初始化时检查Node.js环境
const initNodeCheck = async () => {
  try {
    const result = await nodeAPI.checkNodeEnvironment();
    if (result.success) {
      nodeStatus.value = result.data;
      // 首次静默检查也同步缓存
      localStorage.setItem(NODE_STATUS_KEY, JSON.stringify(result.data));
    }
  } catch (error) {
    console.error('初始化Node.js检查失败:', error);
  }
};
</script>

<style scoped>
.content-overlay {
  position: relative; /* 确保内容层叠在 Vanta.js 背景之上 */
  z-index: 1; /* 高于 Vanta.js 背景 */
  display: flex; /* 启用 Flexbox 布局 */
  justify-content: flex-start; /* 靠左放置 */
  padding-top: 0px; /* 或者其他你需要的值，以便内容不紧贴顶部 */
  padding-left: 80px;
  padding-right: 80px; /* 新增：确保右侧也有相同的内边距 */
  flex-direction: column; /* 垂直排列子元素 */
  height: 100vh; /* 使内容覆盖整个视口高度 */
  overflow: hidden; /* 防止出现双滚动条 */
}

.header {
  display: flex;
  justify-content: flex-end;
  align-items: center; /* 垂直居中对齐 */
  margin-bottom: 10px; /* 增加与下方内容的距离 */
  margin-top: 5px;
  width: 100%; /* 确保 header 占据整个宽度 */
  position: sticky; /* 使 header 保持在顶部 */
  top: 0;
  z-index: 10; /* 确保在其他元素之上 */
  background: transparent; /* 保持背景透明 */
  gap: 4px; /* 缩短按钮之间的间距 */
}

.title {
  position: sticky;
  top: 40px; /* 调整标题固定的位置，在header下方 */
  z-index: 9; /* 低于header但高于内容 */
  background: transparent; /* 保持背景透明 */
  padding-bottom: 10px; /* 添加一些底部间距 */
  text-align: center; /* 使标题文本居中显示 */
  width: 100%; /* 确保标题占据整个宽度 */
}

.main-content {
  flex: 1; /* 使主要内容占据剩余空间 */
  margin-top: -20px;
  overflow-y: auto; /* 只在主内容区域添加滚动 */
  height: calc(100vh - 120px); /* 减去header和title的高度 */
  padding-top: 20px; /* 添加顶部间距 */
  padding-right: 0; /* 移除右侧内边距，使滚动条紧贴右侧边缘 */
  mask-image: linear-gradient(to bottom, transparent 0, black 20px); /* 创建顶部渐变遮罩效果 */
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, black 20px);
  /* 自定义滚动条样式 */
  scrollbar-width: thin; /* Firefox */
  scrollbar-color: rgba(129, 110, 216, 0.5) transparent; /* Firefox */
}

/* 自定义滚动条样式 - Webkit浏览器 */
.main-content::-webkit-scrollbar {
  width: 6px; /* 滚动条宽度 */
}

.main-content::-webkit-scrollbar-track {
  background: transparent; /* 滚动条轨道背景 */
}

.main-content::-webkit-scrollbar-thumb {
  background-color: rgba(129, 110, 216, 0.5); /* 滚动条颜色，使用紫色主题色 */
  border-radius: 10px; /* 圆角 */
  border: 2px solid transparent; /* 边框 */
  background-clip: content-box; /* 背景裁剪到内容框 */
}

.main-content::-webkit-scrollbar-thumb:hover {
  background-color: rgba(129, 110, 216, 0.8); /* 悬停时的颜色 */
}

.card-container {
  display: flex;
  flex-wrap: wrap;
  gap: 60px;
  justify-content: flex-start;
  margin-bottom: 20px;
  row-gap: 0px;
}

/* 现代化按钮样式 */
.modern-button {
  display: flex;
  justify-content: center;
  align-items: center;
  margin: 0 2px; /* 缩短按钮之间的间距 */
}

.btn-modern {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 20px;
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 12px;
  color: white;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  position: relative;
  overflow: hidden;
  min-width: 100px;
}

.btn-modern:hover {
  background: rgba(255, 255, 255, 0.15);
  border-color: rgba(129, 110, 216, 0.5);
  transform: translateY(-2px);
  box-shadow: 0 8px 25px rgba(129, 110, 216, 0.3);
}

.btn-modern:active {
  transform: translateY(0);
  box-shadow: 0 4px 15px rgba(129, 110, 216, 0.2);
}

.btn-icon {
  width: 18px;
  height: 18px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.btn-icon svg {
  width: 100%;
  height: 100%;
  transition: transform 0.3s ease;
}

.btn-modern:hover .btn-icon svg {
  transform: scale(1.1);
}

.btn-text {
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
  letter-spacing: 0.5px;
}

/* 特定按钮的颜色主题 */
.market-btn:hover {
  border-color: rgba(76, 175, 80, 0.5);
  box-shadow: 0 8px 25px rgba(76, 175, 80, 0.3);
}

.settings-btn:hover {
  border-color: rgba(255, 152, 0, 0.5);
  box-shadow: 0 8px 25px rgba(255, 152, 0, 0.3);
}

.node-btn:hover {
  border-color: rgba(33, 150, 243, 0.5);
  box-shadow: 0 8px 25px rgba(33, 150, 243, 0.3);
}

.node-btn.checking {
  background: rgba(33, 150, 243, 0.2);
  border-color: rgba(33, 150, 243, 0.4);
}

.node-btn.checking .btn-icon svg {
  animation: pulse 2s infinite;
}

@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.5; }
}

/* 响应式设计 */
@media (max-width: 768px) {
  .btn-modern {
    padding: 10px 16px;
    font-size: 13px;
    min-width: 80px;
  }
  
  .btn-icon {
    width: 16px;
    height: 16px;
  }
}

.btn-modern.start {
  top: -50%;
  transform: rotate(5deg);
}

.mktbutton button.start p:after {
  color: white;
  transition: all 0s ease;
  content: attr(data-start);
  top: 50%;
  transform: translateY(-50%);
  animation: start 0.3s ease;
  animation-fill-mode: forwards;
}

@keyframes start {
  from {
    top: -50%;
  }
}

.mktbutton button.start:hover:before, .mktbutton button.start:hover:after {
  display: none;
}

.mktbutton button.start:hover span {
  display: none;
}

.mktbutton button:active {
  outline: none;
  border: none;
}

.mktbutton button:focus {
  outline: 0;
}

/* 分类区域动画 */
.category-group {
  margin-bottom: -60px;
}

.category-animate {
  animation: fadeInUp 0.8s ease-out both;
}

.category-header {
  margin: 0.5rem 0;
  padding-left: 1rem;
  border-left: 4px solid #000000;
  text-align: left;
}

.category-title-animate {
  animation: slideInFromLeft 0.6s ease-out both;
}

/* 插件卡片动画 */
.plugin-card-animate {
  animation: fadeInUp 0.6s ease-out both;
}

/* 空状态动画 */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 80px 20px;
  text-align: center;
  min-height: 400px;
}

.empty-state-animate {
  animation: scaleIn 0.8s ease-out both;
  animation-delay: 0.3s;
}

.empty-icon {
  font-size: 64px;
  margin-bottom: 24px;
  opacity: 0.6;
}

.empty-state h3 {
  font-size: 24px;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.8);
  margin: 0 0 12px 0;
}

.empty-state p {
  font-size: 16px;
  color: rgba(255, 255, 255, 0.6);
  margin: 0 0 32px 0;
  max-width: 400px;
}

.empty-action-btn {
  padding: 14px 28px;
  background: linear-gradient(135deg, #6366f1 0%, #a855f7 100%);
  border: none;
  border-radius: 12px;
  color: white;
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  box-shadow: 0 8px 25px rgba(99, 102, 241, 0.3);
}

.empty-action-btn:hover {
  transform: translateY(-3px);
  box-shadow: 0 12px 35px rgba(99, 102, 241, 0.4);
  background: linear-gradient(135deg, #5b5ff0 0%, #a048f5 100%);
}

/* Node.js检查按钮样式 */
.mktbutton button.checking {
  background-color: #f39c12 !important;
  animation: pulse 1.5s infinite;
}

.mktbutton button.checking:before,
.mktbutton button.checking:after {
  background-color: #f39c12 !important;
}

.mktbutton button.checking span:before,
.mktbutton button.checking span:after {
  background-color: #f39c12 !important;
}

@keyframes pulse {
  0% {
    box-shadow: 0 0 0 0 rgba(243, 156, 18, 0.7);
  }
  70% {
    box-shadow: 0 0 0 10px rgba(243, 156, 18, 0);
  }
  100% {
    box-shadow: 0 0 0 0 rgba(243, 156, 18, 0);
  }
}

/* 页面加载动画关键帧 */
@keyframes fadeInUp {
  from {
    opacity: 0;
    transform: translateY(30px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes slideInFromLeft {
  from {
    opacity: 0;
    transform: translateX(-30px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@keyframes scaleIn {
  from {
    opacity: 0;
    transform: scale(0.9);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

/* Node.js安装确认对话框样式 */
:global(.node-install-confirm) {
  min-width: 300px !important;
}

:global(.node-install-confirm .el-message__content) {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}
</style>
